package com.tianji.learning.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.client.course.CatalogueClient;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.IdAndNumDTO;
import com.tianji.api.dto.course.CataSimpleInfoDTO;
import com.tianji.api.dto.course.CourseFullInfoDTO;
import com.tianji.api.dto.course.CourseSimpleInfoDTO;

import com.tianji.common.domain.dto.PageDTO;
import com.tianji.common.domain.query.PageQuery;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.utils.*;

import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.domain.vo.LearningLessonVO;
import com.tianji.learning.domain.vo.LearningPlanPageVO;
import com.tianji.learning.domain.vo.LearningPlanVO;
import com.tianji.learning.enums.LessonStatus;
import com.tianji.learning.enums.PlanStatus;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningLessonService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 学生课程表 服务实现类
 * </p>
 *
 * @author 爱坤的代码
 * @since 2024-08-20
 */
@Service
@SuppressWarnings("ALL")
@Slf4j
@RequiredArgsConstructor
public class LearningLessonServiceImpl extends ServiceImpl<LearningLessonMapper, LearningLesson> implements ILearningLessonService {


    private final CourseClient courseClient;
    private final CatalogueClient catalogueClient;
    private final LearningRecordMapper recordMapper;

    @Override
    @Transactional
    public void addUserLessons(Long userId, List<Long> courseIds) {
        // 根据课程ID列表查询课程有效期等简单信息
        List<CourseSimpleInfoDTO> cInfoList = courseClient.getSimpleInfoList(courseIds);
        // 如果课程信息列表为空，则记录日志并返回
        if (CollUtils.isEmpty(cInfoList)) {
            log.info("课程不存在,无法添加到课程");
            return;
        }
        // 初始化存储用户课程关系实体的列表
        List<LearningLesson> list = new ArrayList<>(cInfoList.size());
        // 遍历课程信息列表，构建用户课程关系实体
        for (CourseSimpleInfoDTO cInfo : cInfoList) {
            LearningLesson lesson = new LearningLesson();
            // 如果课程有效期大于0，则设置有效期起止时间
            Integer validDuration = cInfo.getValidDuration();
            if (validDuration != null && validDuration > 0) {
                LocalDateTime now = LocalDateTime.now();
                lesson.setCreateTime(now);
                lesson.setExpireTime(now.plusMonths(validDuration));
            }
            // 设置用户ID和课程ID
            lesson.setUserId(userId);
            lesson.setCourseId(cInfo.getId());
            // 将构建的用户课程关系实体添加到列表中
            list.add(lesson);
        }
        // 批量保存用户课程关系实体
        saveBatch(list);
    }

    @Override
    public PageDTO<LearningLessonVO> queryMyLessons(PageQuery query) {
        // 1.获取当前登录用户
        Long user = UserContext.getUser();
        //分页查询
        Page<LearningLesson> page = lambdaQuery()
                .eq(LearningLesson::getUserId, user) // where user_id = #{userId}
                .page(query.toMpPage("latest_learn_time", false));
        List<LearningLesson> records = page.getRecords();
        if (CollUtils.isEmpty(records)) {
            return PageDTO.empty(page);
        }
        //查询课程信息
        Map<Long, CourseSimpleInfoDTO> cMap = queryCourseSimpleInfoList(records);

        //封装vo返回
        List<LearningLessonVO> list = new ArrayList<>(records.size());
        //循环遍历，把LearningLesson转为VO
        for (LearningLesson r : records) {
            LearningLessonVO learningLessonVO = BeanUtils.copyProperties(r, LearningLessonVO.class);
            //获取课程信息填充到vo
            CourseSimpleInfoDTO courseSimpleInfo = cMap.get(r.getCourseId());
            learningLessonVO.setCourseName(courseSimpleInfo.getName());
            learningLessonVO.setCourseCoverUrl(courseSimpleInfo.getCoverUrl());
            learningLessonVO.setSections(courseSimpleInfo.getSectionNum());
            list.add(learningLessonVO);


        }
        return PageDTO.of(page, list);
    }

    @Override
    public LearningLessonVO queryMyLessonNow() {
        // 1.获取当前登录的用户
        Long user = UserContext.getUser();
        // 2.查询正在学习的课程 select * from xx where user_id = #{userId} AND status = 1 order by latest_learn_time limit 1
        LearningLesson one = lambdaQuery()
                .eq(LearningLesson::getUserId, user)
                .eq(LearningLesson::getStatus, LessonStatus.LEARNING.getValue())
                .orderByDesc(LearningLesson::getLatestLearnTime)
                .last("limit 1")
                .one();
        if (one == null) {
            return null;
        }
        // 3.拷贝PO基础属性到VO
        LearningLessonVO vo = BeanUtils.copyProperties(one, LearningLessonVO.class);
        // 4.查询课程信息
        CourseFullInfoDTO cInfo = courseClient.getCourseInfoById(one.getCourseId(), false, false);
        if (cInfo == null) {
            throw new BadRequestException("课程信息不存在");
        }
        vo.setCourseName(cInfo.getName());
        vo.setCourseCoverUrl(cInfo.getCoverUrl());
        vo.setSections(cInfo.getSectionNum());
        // 5.统计课表中的课程数量 select count(1) from xxx where user_id = #{userId}
        Integer countse = lambdaQuery()
                .eq(LearningLesson::getUserId, user)
                .count();
        vo.setCourseAmount(countse);
        // 6.查询小节信息
        List<CataSimpleInfoDTO> sections = catalogueClient.batchQueryCatalogue(CollUtils.singletonList(one.getLatestSectionId()));
        if (!CollUtils.isEmpty(sections)) {
            CataSimpleInfoDTO cataSimpleInfoDTO = sections.get(0);
            vo.setLatestSectionName(cataSimpleInfoDTO.getName());
            vo.setLatestSectionIndex(cataSimpleInfoDTO.getCIndex());
        }
        return vo;
    }

    @Override
    public LearningLessonVO queryMyLessonByCourseId(Long courseId) {
        // 1.获取当前登录用户
        Long userId = UserContext.getUser();
        // 2.查询课程信息 select * from xx where user_id = #{userId} AND course_id = #{courseId}
        LearningLesson lesson = getOne(buildUserIdAndCourseIdWrapper(userId, courseId));
        if (lesson == null) {
            return null;
        }
        // 3.处理VO
        return BeanUtils.copyBean(lesson, LearningLessonVO.class);
    }

    @Override
    public void deleteMyLessonByCourseId(Long courseId, Long userId) {
        //获取当前登录用户
        if (userId == null) {
            userId = UserContext.getUser();
        }
        //2.删除
        remove(buildUserIdAndCourseIdWrapper(userId, courseId));


    }


    private Map<Long, CourseSimpleInfoDTO> queryCourseSimpleInfoList(List<LearningLesson> records) {
        //获取课程id
        Set<Long> cIds = records.stream().map(LearningLesson::getCourseId).collect(Collectors.toSet());
        // 3.2.查询课程信息
        List<CourseSimpleInfoDTO> courseSimpleInfoList = courseClient.getSimpleInfoList(cIds);
        if (CollUtils.isEmpty(courseSimpleInfoList)) {
            throw new BadRequestException("课程信息不存在");
        }
        // 3.3.把课程集合处理成Map，key是courseId，值是course本身
        Map<Long, CourseSimpleInfoDTO> collect = courseSimpleInfoList.stream()
                .collect(Collectors.toMap(CourseSimpleInfoDTO::getId, c -> c));
        return collect;
    }

    @Override
    public LearningLesson queryByUserAndCourseId(Long userId, Long courseId) {
        return getOne(buildUserIdAndCourseIdWrapper(userId, courseId));
    }

    @Override
    public LearningPlanPageVO queryMyPlans(PageQuery query) {
        LearningPlanPageVO resulf = new LearningPlanPageVO();
        //获取当前的用户登录
        Long userId = UserContext.getUser();
        // 2.获取本周起始时间
        LocalDate now = LocalDate.now();
        LocalDateTime begin = DateUtils.getWeekBeginTime(now);
        LocalDateTime end = DateUtils.getWeekEndTime(now);
        // 3.查询总的统计数据
        // 3.1.本周总的已学习小节数量
        Integer weekFinished = recordMapper.selectCount(new LambdaQueryWrapper<LearningRecord>()
                .eq(LearningRecord::getUserId, userId)
                .eq(LearningRecord::getFinished, true)
                .gt(LearningRecord::getFinishTime, begin)
                .lt(LearningRecord::getFinishTime, end)
        );
        resulf.setWeekFinished(weekFinished);
        // 3.2.本周总的计划学习小节数量
        Integer weekTotalPlan = getBaseMapper().queryTotalPlan(userId);
        resulf.setWeekTotalPlan(weekTotalPlan);

        // TODO 3.3.本周学习积分

        // 4.查询分页数据
        // 4.1.分页查询课表信息以及学习计划信息
        Page<LearningLesson> page = lambdaQuery()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getPlanStatus, PlanStatus.PLAN_RUNNING)
                .in(LearningLesson::getStatus, LessonStatus.NOT_BEGIN, LessonStatus.LEARNING)
                .page(query.toMpPage("latest_learn_time", false));

        List<LearningLesson> list = page.getRecords();
        if (CollUtils.isEmpty(list)) {
            return resulf.emptyPage(page);
        }
        //查询课表对应的课程信息
        Map<Long, CourseSimpleInfoDTO> courseSimpleInfoList = queryCourseSimpleInfoList(list);
        //统计每一个课程本周的学习小节数量
        List<IdAndNumDTO> listId = recordMapper.countLearnedSections(userId, begin, end);
        Map<Long, Integer> countMap = IdAndNumDTO.toMap(listId);
        // 4.4.组装数据VO
        List<LearningPlanVO> voList = new ArrayList<>(list.size());
        for (LearningLesson lesson : list) {
            // 4.4.1.拷贝基础属性到vo
            LearningPlanVO vo = BeanUtils.copyBean(lesson, LearningPlanVO.class);
            // 4.4.2.填充课程详细信息
            CourseSimpleInfoDTO cInfo = courseSimpleInfoList.get(lesson.getCourseId());
            if (cInfo != null) {
                vo.setCourseName(cInfo.getName());
                vo.setSections(cInfo.getSectionNum());
            }
            // 4.4.3.每个课程的本周已学习小节数量
            vo.setWeekLearnedSections(countMap.getOrDefault(lesson.getId(), 0));
            voList.add(vo);
        }
        return resulf.pageInfo(page.getTotal(), page.getPages(), voList);


    }

    @Override
    public void createLearningPlan(Long courseId, Integer freq) {
        //获取当前用户登录
        Long userId = UserContext.getUser();
        //1.查询课表中指定的课程有关的数据
        LearningLesson lesson = queryByUserAndCourseId(userId, courseId);
        AssertUtils.isNotNull(lesson, "课程不存在");
        //修改数据
        LearningLesson l = new LearningLesson();
        l.setId(lesson.getId());
        l.setWeekFreq(freq);
        if (lesson.getPlanStatus() == PlanStatus.NO_PLAN) {
            l.setPlanStatus(PlanStatus.PLAN_RUNNING);
        }
        updateById(l);


    }

    @Override
    public Long isLessonValid(Long courseId) {
        //获取当前登录用户
        Long userId = UserContext.getUser();
        if (userId == null) {
            return null;
        }
        //2.查询课表信息
        LearningLesson one = getOne(buildUserIdAndCourseIdWrapper(userId, courseId));
        if (one == null) {
            return null;
        }
        return one.getId();


    }

    @Override
    public Integer countLearningLessonByCourse(Long courseId) {
       return lambdaQuery()
               .eq(LearningLesson::getCourseId, courseId)
               .in(LearningLesson::getStatus,
                       LessonStatus.NOT_BEGIN,
                       LessonStatus.LEARNING)
               .count();
    }


    /**
     * 构建用户ID和课程ID的查询包装器
     * 该方法用于生成一个LambdaQueryWrapper，用于后续的数据库查询操作
     * 主要用于根据用户ID和课程ID进行精确查询
     *
     * @param userId   用户ID，用于查询特定用户的数据
     * @param courseId 课程ID，用于查询特定课程的数据
     * @return 返回构建的LambdaQueryWrapper对象，该对象包含了对用户ID和课程ID的查询条件
     */
    private LambdaQueryWrapper<LearningLesson> buildUserIdAndCourseIdWrapper(Long userId, Long courseId) {
        // 创建一个LambdaQueryWrapper对象，并设置查询条件：用户ID和课程ID
        LambdaQueryWrapper<LearningLesson> queryWrapper = new QueryWrapper<LearningLesson>()
                .lambda()
                .eq(LearningLesson::getUserId, userId)
                .eq(LearningLesson::getCourseId, courseId);
        return queryWrapper;
    }


}
